package cn.bugstack.mybatis.scripting.xmltags;

import cn.bugstack.mybatis.parsing.GenericTokenParser;
import cn.bugstack.mybatis.parsing.TokenHandler;
import cn.bugstack.mybatis.type.SimpleTypeRegistry;

import java.util.regex.Pattern;

/**
 * 文本sql节点
 * @author xingshujing
 */
public class TextSqlNode implements SqlNode{

    /**
     * sql文本
     */
    private String text;

    /**
     * 正则表达式 判断text是否符合规则
     */
    private Pattern injectionFilter;

    public TextSqlNode(String text, Pattern injectionFilter) {
        this.text = text;
        this.injectionFilter = injectionFilter;
    }

    public TextSqlNode(String text) {
        this(text, null);
    }

    @Override
    public boolean apply(DynamicContext context) {
        // 解析 ${}中的ognl表达式，替换成对应的值
        GenericTokenParser tokenParser = new GenericTokenParser("${", "}", new BindingTokenParser(context, injectionFilter));
        context.appendSql(tokenParser.parse(text));
        return true;
    }

    public boolean isDynamic() {
        DynamicCheckerTokenParser dynamicCheckerTokenParser = new DynamicCheckerTokenParser();
        // 检查 TextSqlNode 中 的sql文本是否含有 ${}
        GenericTokenParser tokenParser = new GenericTokenParser("${", "}", dynamicCheckerTokenParser);
        tokenParser.parse(text);
        return dynamicCheckerTokenParser.isDynamic;
    }

    private class BindingTokenParser implements TokenHandler {
        /**
         * context 中存储的入参，用于
         */
        private DynamicContext context;

        /**
         * 正则表达式 判断text是否符合规则
         */
        private Pattern injectionFilter;
        public BindingTokenParser(DynamicContext context, Pattern injectionFilter) {
            this.context = context;
            this.injectionFilter = injectionFilter;
        }

        /**
         * 使用ognl表达式解析content，并返回结果
         * @param content #{} ${}
         * @return
         */
        @Override
        public String handleToken(String content) {
            // 获取入参
            Object parameter = context.getBindings().get("_parameter");
            if (parameter == null) {
                context.getBindings().put("value", null);
            } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
                context.getBindings().put("value", parameter);
            }
            // 从缓存里取得值
            Object value = OgnlCache.getValue(content, context.getBindings());
            String srtValue = (value == null ? "" : String.valueOf(value)); // issue #274 return "" instead of "null"
            // 校验 是否符合正则表达式(比如是否有sql注入)
            checkInjection(srtValue);
            return srtValue;
        }
        // 检查是否匹配正则表达式
        private void checkInjection(String value) {
            if (injectionFilter != null && !injectionFilter.matcher(value).matches()) {
                throw new RuntimeException("Invalid input. Please conform to regex" + injectionFilter.pattern());
            }
        }
    }

    private class DynamicCheckerTokenParser implements TokenHandler {
        private boolean isDynamic;
        @Override
        public String handleToken(String content) {
            isDynamic = true;
            return null;
        }
    }
}
